import numpy as np
from scipy import stats
import seaborn as sns
import numpy.random as npr
import matplotlib

matplotlib.use(backend="TkAgg")
import matplotlib.pyplot as plt

plt.rcParams['font.family'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False

n_samples = 10000
continuous_part = npr.normal(loc=0, scale=1, size=int(0.8 * n_samples))
discrete_part = np.repeat([-2, -1, 0, 1, 2], int(0.2 * n_samples / 5))
data = np.concatenate([continuous_part, discrete_part], axis=0)

'''
对于第k个样本x_(k),y_ecdf[k-1]=k/n,这恰好表示经验分布函数F_n(x)
在点x=x_k(n)处的值（右连续定义：F_n(x)=(# samples<=x)/n）
'''
x_sorted = np.sort(data)
y_ecdf = np.arange(start=1, stop=len(x_sorted) + 1) / len(x_sorted)

plt.figure(figsize=(12, 6))
'''
where='post'：表示阶梯在 x 值之后跳变（即右侧跳变）。
这和经验分布函数的右连续定义一致：F(x) = P(X ≤ x) 
是在观测值处向右跳（值在点上等于包含该点的累计比例）
'''
plt.step(x=x_sorted, y=y_ecdf, where='post', alpha=0.7, label='Empirical CDF')
plt.xlabel('x')
plt.ylabel('CDF')
plt.title('Empirical CDF')
plt.grid(visible=True, alpha=0.3)

# 标记几个不连续点
discrete_points = [-2, -1, 0, 1, 2]
for point in discrete_points:
    idx = np.where(x_sorted == point)[0]
    if len(idx) > 0:
        jump_idx = idx[-1]
        left_limit = y_ecdf[jump_idx] if jump_idx == 0 else y_ecdf[jump_idx - 1]
        right_limit = y_ecdf[jump_idx]

        plt.plot([point, point], [left_limit, right_limit], 'r--', linewidth=2)
        plt.plot(point, left_limit, 'ro', markersize=4, label='F(x-0)' if point == -2 else "")
        plt.plot(point, right_limit, 'go', markersize=4, label='F(x+0)' if point == -2 else "")

        print(f"在 x={point} 处:")
        print(f"  F({point}-0) = {left_limit:.4f}")
        print(f"  F({point}+0) = {right_limit:.4f}")
        print(f"  跳变大小: {right_limit - left_limit:.4f}")

plt.legend()
plt.tight_layout()
plt.show()
